#pragma once

#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "error.hpp"

namespace ns_server
{

    const static uint16_t default_port = 8080;
    using func_t = std::function<std::string(std::string)>; // 参数为string返回值为string的函数

    class UdpServer
    {
    public:
        UdpServer(func_t cb,uint16_t port = default_port) : _service(cb),_port(port)
        {
            std::cout << "server addr: " << _port << std::endl;
        }
        ~UdpServer() {}
        void InitServer()
        {
            // 1.创建socket接口，打开网络文件
            _sock = socket(AF_INET, SOCK_DGRAM, 0);
            // AF_INET表示使用IPv4，SOcCK_DGRAM表示位UDP协议
            if (_sock < 0)
            {
                std::cerr << "create socket error:" << strerror(errno) << std::endl;
                exit(SOCKET_ERR);
            }
            std::cout << "create socket success:" << _sock << std::endl;

            // 2.给服务器指明IP地址和Port
            struct sockaddr_in local; // 该local在用户空间的特定函数的栈帧上定义
            bzero(&local, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port); // 端口号。这个数据会出现在网络中，因此要使用htons改为网络序列

            // 将字符串转换为4字节整数，不能强转（强转不改变2进制序列）
            // local.sin_addr.s_addr=inet_addr(_ip.c_str());//将主机序列转换为网络序列

            // 由于是云服务器上进行开发，不需要指明某一个确定的IP，因此如下更改
            local.sin_addr.s_addr = INADDR_ANY; // 让UDP_server启动时bind服务器上任一ip

            // 3.绑定
            // 为什么需要自己bind，server的端口不能随意改变，同一家公司的port号需要统一规范化
            if (bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                std::cerr << "bind socket error:" << strerror(errno) << std::endl;
                exit(BIND_ERR);
            }
            std::cout << "bind socket success:" << _sock << std::endl;
        }
        void Start()
        {
            char buffer[1024]; // 默认缓冲区为1024字节
            while (true)       // 服务器不会退出
            {
                // 接收
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int n = recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len); // 大小-1是为了预留\0的位置
                if (n > 0)
                {
                    buffer[n] = '\0'; // 结尾补\0
                }
                else
                    continue;

                std::string clentip = inet_ntoa(peer.sin_addr); // 将4字节网络字节序ip转化为点分十进制字符串ip地址
                uint16_t clientprot = ntohs(peer.sin_port);     // 将16位数由网络字节序转换为主机字节序
                std::cout << "get message:" << buffer << std::endl;




                //业务处理
                std::string response =_service(buffer);



                // 发送
                //sendto(_sock, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, sizeof(peer));
                sendto(_sock, response.c_str(), response.size(), 0, (struct sockaddr *)&peer, sizeof(peer));

            }
        }

    private:
        int _sock;
        uint16_t _port;
        func_t _service; // 服务器提供的其他服务，由服务器传入函数进行回调
        // std::string _ip;云服务器无需ip
    };
}